
#include "List.h"

C_List_Node_Ptr
C_List_Node_New(void)
{
	C_List_Node_Ptr p = C_Malloc(sizeof(C_List_Node));
	if (p != NULL)
	{
		p->Next = p->Prev = NULL;
		p->Data = NULL;
	}
	return p;
}

void
C_List_Node_Free(C_List_Node_Ptr p)
{
	if (p != NULL) C_Free(p);
}

C_List_Ptr
C_List_New(void)
{
	C_List_Ptr p = C_Malloc(sizeof(C_List));
	if (p != NULL)
	{
		p->Head = p->Tail = NULL;
		p->Free = NULL;
		p->Size = 0;
	}
	return p;
}

void
C_List_Free(C_List_Ptr p)
{
	if (p != NULL)
	{
		C_List_Clear(p);
		C_Free(p);
	}
}

C_Error_t
C_List_Set_Mem_Free(C_List_Ptr p, C_Mem_Free_t f)
{
	if (p == NULL)
		return C_ERR_INVALID_PARAMS;
	p->Free = f;
	return C_ERR_NOERR;
}

C_Error_t
C_List_Insert(C_List_Ptr p, void *data, C_List_Node_Ptr node)
{
	C_List_Node_Ptr	n;

	if (p == NULL)
		return C_ERR_INVALID_PARAMS;

	n = C_List_Node_New();

	if (n == NULL)
		return C_ERR_NOMEM;

	n->Data = data;

	if (p->Size > 0)
	{
		if (node == NULL) // append
		{
			n->Prev = p->Tail;
			p->Tail->Next = n;
			p->Tail = n;
		}
		else
		{
			n->Next = node;
			n->Prev = node->Prev;
			if (node->Prev != NULL)
			{
				node->Prev->Next = n;
			}
			else
			{
				p->Head = n;
			}
			node->Prev = n;
		}
	}
	else
	{
		p->Head = p->Tail = n;
	}

	p->Size++;

	return C_ERR_NOERR;
}

C_Error_t
C_List_Get_Head(C_List_Ptr p, C_List_Node_Ptr *n)
{
	if (p == NULL || n == NULL)
		return C_ERR_INVALID_PARAMS;
	*n = p->Head;
	return C_ERR_NOERR;
}

C_Error_t
C_List_Get_Tail(C_List_Ptr p, C_List_Node_Ptr *n)
{
	if (p == NULL || n == NULL)
		return C_ERR_INVALID_PARAMS;
	*n = p->Tail;
	return C_ERR_NOERR;
}

C_Error_t
C_List_Remove(C_List_Ptr p, C_List_Node_Ptr n, void **data)
{
	if (p == NULL || n == NULL)
		return C_ERR_INVALID_PARAMS;

	if (p->Size == 0)
		return C_ERR_INVALID_STATE;

	if (n->Next != NULL)
	{
		n->Next->Prev = n->Prev;
	}
	else
	{
		p->Tail = n->Prev;
	}

	if (n->Prev != NULL)
	{
		n->Prev->Next = n->Next;
	}
	else
	{
		p->Head = n->Next;
	}

	if (data != NULL)
		*data = n->Data;

	p->Size--;

	C_Free(n);

	return C_ERR_NOERR;
}

C_Error_t
C_List_Destroy(C_List_Ptr p, C_List_Node_Ptr n)
{
	C_Error_t err;
	void *data;

	err = C_List_Remove(p, n, &data);

	if (err == C_ERR_NOERR)
	{
		if (p->Free != NULL)
			p->Free(data);
	}

	return err;
}

C_Error_t
C_List_Clear(C_List_Ptr p)
{
	if (p == NULL)
		return C_ERR_INVALID_PARAMS;

	while (p->Size > 0)
	{
		C_List_Destroy(p, p->Tail);
	}

	return C_ERR_NOERR;
}
